Skip to content

feat: pass invocation_state to edge condition calls#2305

Merged
zastrowm merged 3 commits into
strands-agents:mainfrom
yananym:feat/edge-condition-invocation-state
May 29, 2026
Merged

feat: pass invocation_state to edge condition calls#2305
zastrowm merged 3 commits into
strands-agents:mainfrom
yananym:feat/edge-condition-invocation-state

Conversation

@yananym

@yananym yananym commented May 19, 2026

Copy link
Copy Markdown
Contributor

Description

Add support for edge conditions that receive invocation_state, enabling conditional routing based on runtime context (feature flags, user roles, environment config) passed during
graph invocation.

Also fixes a deadlock in _compute_ready_nodes_for_resume() where conditional edges evaluating to False would block downstream nodes from ever becoming ready on interrupt/resume workflows.

Session persistence of invocation_state

When a SessionManager is configured, invocation_state is serialized alongside graph state so that resumed executions retain the same runtime context. This is validated as JSON-serializable on entry.

Example: A graph with a feature-flag condition:

def route_if_premium(state, *, invocation_state, **kwargs):
return invocation_state.get("user_tier") == "premium"

graph(task="process order", invocation_state={"user_tier": "premium"})

If the graph is interrupted and later resumed from session, invocation_state is restored so route_if_premium still evaluates correctly without the caller needing to re-supply it.

Public API Changes

GraphBuilder.add_edge() now accepts conditions with an extended signature via the EdgeConditionWithContext Protocol:

# Legacy (still works, no changes needed)
def my_condition(state: GraphState) -> bool:
    return len(state.completed_nodes) > 0

# New: receives invocation_state for runtime routing decisions
def my_condition(state: GraphState, *, invocation_state: dict, **kwargs) -> bool:
    return invocation_state.get("role") == "admin"

builder.add_edge("router", "admin_path", condition=my_condition)
graph("task", invocation_state={"role": "admin"})

Both signatures are supported indefinitely — no deprecation, no breaking changes.

Related Issues

Resolves #1346

Documentation PR

strands-agents/docs#847

Type of Change

New feature

Testing

How have you tested the change?

  • 21 new unit tests covering protocol dispatch, invocation_state propagation, resume deadlock fix, and serialization round-trip
  • 6 new integration tests covering conditional routing, backwards compat, combined conditions, diamond convergence, persistence/resume, and streaming events
  • All 49 existing graph unit tests pass unchanged (backwards compatibility verified)
  • 95% line+branch coverage on src/strands/multiagent/graph.py
  • ruff check and mypy pass clean
  • I ran hatch run prepare

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Comment thread src/strands/multiagent/graph.py
Comment thread src/strands/multiagent/graph.py Outdated
@github-actions

Copy link
Copy Markdown
Contributor

Issue: The PR introduces a public API change (GraphBuilder.add_edge() accepts a new condition signature, new types EdgeCondition and EdgeConditionWithContext are exported) but doesn't appear to have a needs-api-review label. Per the API bar-raising process, new public abstractions customers will use should be flagged for API review.

Suggestion: Consider adding the needs-api-review label. In particular, the API reviewer should evaluate:

  • Whether EdgeConditionWithContext is the right naming (vs. e.g., ContextualEdgeCondition)
  • Whether requiring **kwargs in the protocol is the right extensibility pattern vs. a versioned protocol approach
  • Whether the invocation_state: dict[str, Any] type should be dict[str, Any] | None to match the caller side more closely

Comment thread strands-py/src/strands/multiagent/graph.py
Comment thread strands-py/src/strands/multiagent/graph.py
Comment thread tests/strands/multiagent/test_graph.py Outdated
@github-actions

Copy link
Copy Markdown
Contributor

Assessment: Comment

Well-designed feature that cleanly extends the existing edge condition system with invocation_state context while maintaining full backwards compatibility. The protocol-based approach with inspect.signature() dispatch is a solid technical choice.

Review Categories
  • Performance: inspect.signature() is called on every should_traverse() without caching — worth addressing for graphs with many conditional edges or cyclic execution patterns.
  • API Surface: New public types (EdgeCondition, EdgeConditionWithContext) are exported but the PR may benefit from formal API review per the bar-raising process, particularly around naming and the protocol's type design choices.
  • Serialization: invocation_state is persisted without validation of serializability — could produce confusing errors for users passing non-serializable values.
  • Testing: Comprehensive test coverage (21 unit + 6 integration tests). Minor type inconsistency with test setup using lists where sets are expected.

The deadlock fix in _compute_ready_nodes_for_resume is a valuable correctness improvement alongside the main feature.

@yananym

yananym commented May 20, 2026

Copy link
Copy Markdown
Contributor Author

Issue: The PR introduces a public API change (GraphBuilder.add_edge() accepts a new condition signature, new types EdgeCondition and EdgeConditionWithContext are exported) but doesn't appear to have a needs-api-review label. Per the API bar-raising process, new public abstractions customers will use should be flagged for API review.

Suggestion: Consider adding the needs-api-review label. In particular, the API reviewer should evaluate:

I do not think i have permissions to add labels to this PR

@zastrowm zastrowm self-requested a review May 26, 2026 14:22
Comment thread src/strands/multiagent/graph.py Outdated
Comment thread src/strands/multiagent/graph.py Outdated
Comment thread strands-py/src/strands/multiagent/graph.py
Comment thread strands-py/src/strands/multiagent/__init__.py
@yonib05 yonib05 added area-multiagent Multi-agent related area-hooks Features or requests that might be implementable via hooks labels May 27, 2026
@github-actions github-actions Bot added size/l and removed size/l labels May 27, 2026
@yananym yananym requested a deployment to manual-approval May 27, 2026 23:34 — with GitHub Actions Waiting
@yananym yananym requested a review from zastrowm May 27, 2026 23:39
yananym added 3 commits May 27, 2026 23:42
Add support for edge conditions that receive invocation_state,
enabling conditional routing based on runtime context (feature flags,
user roles, environment config) passed during graph invocation.

Also fixes a deadlock in _compute_ready_nodes_for_resume() where
conditional edges evaluating to False would block downstream nodes
from ever becoming ready on interrupt/resume workflows.

Resolves strands-agents#1346
- Cache inspect.signature() results via WeakKeyDictionary
- Remove unused @runtime_checkable decorator
- Gate serialization validation on session_manager presence
- Add validation on deserialization path for symmetry
- Move json import to module level
- Add inline comments for short-circuit and cache behavior
- Extract _make_graph() test helper, fix list->set type consistency
Cache the result of _is_context_condition() on each GraphEdge instance
instead of a module-level WeakKeyDictionary. Simpler, no global state,
and directly co-located with the edge that owns the condition.
@yananym yananym force-pushed the feat/edge-condition-invocation-state branch from 97d4187 to 2d0cbd9 Compare May 27, 2026 23:43
@github-actions github-actions Bot removed the size/l label May 27, 2026
@yananym yananym requested a deployment to manual-approval May 27, 2026 23:44 — with GitHub Actions Waiting
@yananym yananym requested a deployment to manual-approval May 27, 2026 23:44 — with GitHub Actions Waiting
@zastrowm zastrowm enabled auto-merge (squash) May 29, 2026 14:25
@codecov

codecov Bot commented May 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
strands-py/src/strands/multiagent/graph.py 91.48% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-hooks Features or requests that might be implementable via hooks area-multiagent Multi-agent related size/l

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Pass invocation_state to edge condition call

3 participants